using System;
using System.Drawing;
using System.Collections.Generic;
using Clandestine.Graphics;

namespace Clandestine.Engines.Field
{
	// LayerGroup where layers are flattened into one, nice and easy sprite, i.e. no MegaQuads
	public class StaticLayerGroup : LayerGroup
	{
		public Sprite Sprite { get; private set; }
		public override Point Position 
		{
			get { return base.Position; }
			set 
			{
				base.Position = value; 
				this.positionSprite(this.MinX, this.MinY); 
			}
		}
		
		private int depth = 0;
		public override int Depth
		{
			get { return depth; }
			set
			{
				if (Sprite != null)
					Sprite.Depth = value;
				depth = value;
			}
		}
	
		public StaticLayerGroup(RenderList r, Map map, object parent) : base(r, map, parent)
		{
			
		}
		
		public override List<Renderable> GetRenderables()
		{
			return new List<Renderable>() { this.Sprite };
		}
		
		public override void RedrawDirty ()
		{
			if (this.Dirty)
				this.RedrawSprite();	
			this.Dirty = false;	
		}
		
		public void RedrawSprite()
		{
			if (!this.Visible)
			{
				this.Sprite.Visible = false;
				return;
			}
				
			int minX = this.MinX;
			int minY = this.MinY;
			int maxX = this.MaxX;
			int maxY = this.MaxY;	
			
			// Create a new RenderList so we don't draw to screen
			RenderList r = new RenderList(this.Graphics);
			for (int y = minY; y <= maxY; y++)
				for (int x = minX; x <= maxX; x++)
					for (int depth = 0; depth < this.Layers.Count; depth++)
					{
						if (this.Layers[depth].Visible)
						{
							// Only draw the real tile if there isn't a temporary one to preview here! -- More accurate previews.
							Tile? tmp = Layers[depth].GetTemporaryTile(x, y);
							if (tmp == null)
								this.drawTile(Layers[depth].GetTile(x, y), r, depth, minX, minY, x, y);
							else
								this.drawTile(tmp, r, depth + 0.01m, minX, minY, x, y);
						}
					}
			
			// Make the texture
			string identifier = System.Guid.NewGuid().ToString();
			Texture tex = this.Graphics.RenderToTexture(r, new System.Drawing.Size(((maxX-minX)+1) * this.Map.GridSize, ((maxY-minY)+1)*this.Map.GridSize), identifier);
			
			// Make sprite
			if (Sprite != null)
				Sprite.Dispose();
			Sprite = new Sprite(this.RenderList, tex);
			Sprite.Depth = this.depth;
			this.positionSprite(minX, minY);
			
			// Cleanup
			r.Dispose(); // disposes sprites too
		}
		
		private void positionSprite(int minX, int minY)
		{
			if (Sprite == null)
				return;
			Sprite.Transform.X = (this.Position.X + minX) * this.Map.GridSize;
			Sprite.Transform.Y = (this.Position.Y + minY) * this.Map.GridSize;			
		}
	
		private void drawTile(Tile? t, RenderList r, decimal depth, int minX, int minY, int x, int y)
		{
			if (t != null) 
			{ 
				Sprite s = new Sprite(r, t.Value.Texture);
				s.Depth = depth;
				s.Transform.X = ((x-minX) * base.Map.GridSize);
				s.Transform.Y = ((y-minY) * base.Map.GridSize); 
			}
			// doesn't matter that Sprite goes out of scope, still in render list!
		}
		
		public override void Dispose ()
		{
			if (this.Sprite != null)
				this.Sprite.Dispose();
			base.Dispose();
		}
		
		protected override void OnTileChange(int depth, int x, int y)
		{
			this.Dirty = true;
		}
		
		protected override void OnVisibilityChange()
		{
			this.Dirty = true;
		}
	}
}

